package com.nx.platform.es.bean.modle.query;

import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.primitives.Floats;
import com.nx.platform.es.biz.wrapper.parser.StatementOperator;
import com.nx.platform.es.bean.dto.RequestContext;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.*;

import java.util.List;

/**
 * @author
 * @since 2016年10月13日
 */
public class StringMatchHandler implements QueryFieldHandler {

    @Override
    public void handle(ImmutableMap<String, ?> fieldConfig, RequestContext context, String fieldFace,
                       StatementOperator operator, String fieldValue, ListMultimap<Boolean, QueryBuilder> queryBuilders) {
        //
        if (Strings.isNullOrEmpty(fieldValue)) {
            return;
        }
        // 记录Query信息(Trace)
        context.getTrace().append("&").append(fieldFace).append(operator.getSymbol()).append(fieldValue);
        //
        String face = MapUtils.getString(fieldConfig, "face");
        String fieldName = MapUtils.getString(fieldConfig, "fieldName");
        Preconditions.checkState(!Strings.isNullOrEmpty(fieldName));
        String _type = MapUtils.getString(fieldConfig, "_type");
        String _operator = MapUtils.getString(fieldConfig, "_operator");
        String _minimum_should_match = MapUtils.getString(fieldConfig, "_minimum_should_match");
        String _analyzer = MapUtils.getString(fieldConfig, "_analyzer");
        int alternatives = MapUtils.getIntValue(fieldConfig, "_alternatives", 8);

        // 短语模式 或者 启用了且 或者 使用了匹配度，使用 | 分割关键词
        if ("phrase".equals(_type) || "and".equals(_operator) || !Strings.isNullOrEmpty(_minimum_should_match)) {
            List<String> words = Splitter.on("|").omitEmptyStrings().splitToList(fieldValue);
            if (words.isEmpty()) {
                return;
            }
            if (words.size() == 1) {
                queryBuilders.put(operator.isSign(),
                        build(fieldName, _type, _operator, _minimum_should_match, _analyzer, fieldValue));
            } else if (words.size() <= alternatives) {
                BoolQueryBuilder bool = QueryBuilders.boolQuery();
                for (String word : words) {
                    bool.should(build(fieldName, _type, _operator, _minimum_should_match, _analyzer, word));
                }
                bool.minimumShouldMatch("1");
                queryBuilders.put(operator.isSign(), bool);
            } else {
                throw new IllegalArgumentException("too more alternatives: field=" + face + ", value=" + fieldValue);
            }
        } else {
            queryBuilders.put(operator.isSign(),
                    build(fieldName, _type, _operator, _minimum_should_match, _analyzer, fieldValue));
        }
    }

    private QueryBuilder build(String fieldName, String _type, String _operator, String _minimum_should_match,
            String _analyzer, String fieldValue) {
        if ("phrase".equals(_type)) {
            return matchPhraseQueryBuilder(fieldName, _analyzer, fieldValue);
        } else {
            return matchQueryBuilder(fieldName, _operator, _minimum_should_match, _analyzer, fieldValue);
        }
    }

    private MatchQueryBuilder matchQueryBuilder(String fieldName, String _operator, String _minimum_should_match,
            String _analyzer, String fieldValue) {
        MatchQueryBuilder matchQueryBuilder;
        int index = fieldName.indexOf('^');
        if (index > 0) {
            String field = fieldName.substring(0, index);
            Float boost = Floats.tryParse(fieldName.substring(index + 1));
            matchQueryBuilder = QueryBuilders.matchQuery(field, fieldValue)
                    .boost(boost == null ? 1.0f : boost);
        } else {
            matchQueryBuilder = QueryBuilders.matchQuery(fieldName, fieldValue);
        }
        if (!Strings.isNullOrEmpty(_analyzer)) {
            matchQueryBuilder.analyzer(_analyzer);
        }
        if ("and".equals(_operator)) {
            matchQueryBuilder.operator(Operator.AND);
        } else if (!Strings.isNullOrEmpty(_minimum_should_match)) {
            matchQueryBuilder.minimumShouldMatch(_minimum_should_match);
        }
        return matchQueryBuilder;
    }

    private MatchPhraseQueryBuilder matchPhraseQueryBuilder(String fieldName,
            String _analyzer, String fieldValue) {
        MatchPhraseQueryBuilder matchQueryBuilder;
        int index = fieldName.indexOf('^');
        if (index > 0) {
            String field = fieldName.substring(0, index);
            Float boost = Floats.tryParse(fieldName.substring(index + 1));
            matchQueryBuilder = QueryBuilders.matchPhraseQuery(field, fieldValue)
                    .boost(boost == null ? 1.0f : boost);
        } else {
            matchQueryBuilder = QueryBuilders.matchPhraseQuery(fieldName, fieldValue);
        }
        if (!Strings.isNullOrEmpty(_analyzer)) {
            matchQueryBuilder.analyzer(_analyzer);
        }
        return matchQueryBuilder;
    }

}
